// PHZ
// 2018-5-15

#ifndef XOP_RING_BUFFER_H
#define XOP_RING_BUFFER_H

#include <vector>
#include <atomic>

namespace xop {

template<typename T> class RingBuffer {
public:
	explicit RingBuffer(unsigned capacity = 60)
		: capacity_(capacity), num_datas_(0), buffer_(capacity)
	{
	}

	virtual ~RingBuffer() {}

	bool Push(const T &data) { return PushData(std::forward<T>(data)); }

	bool Push(T &&data) { return PushData(data); }

	bool Pop(T &data)
	{
		if (num_datas_ > 0) {
			data = std::move(buffer_[get_pos_]);
			Add(get_pos_);
			--num_datas_;
			return true;
		}

		return false;
	}

	bool IsFull() const { return num_datas_ == capacity_ ? true : false; }

	bool IsEmpty() const { return num_datas_ == 0 ? true : false; }

	int Size() const { return num_datas_; }

private:
	template<typename F> bool PushData(F &&data)
	{
		if (num_datas_ < capacity_) {
			buffer_[put_pos_] = std::forward<F>(data);
			Add(put_pos_);
			++num_datas_;
			return true;
		}

		return false;
	}

	void Add(int &pos) const { pos = pos + 1 == capacity_ ? 0 : pos + 1; }

	int capacity_ = 0;
	int put_pos_ = 0;
	int get_pos_ = 0;

	std::atomic_int num_datas_;
	std::vector<T> buffer_;
};

}

#endif
